package org.apache.sad.core.framework.logging.log4j;

import java.io.IOException;
import java.io.Writer;

public class AuditLoggerWriter extends Writer {
	private Writer out;

	private char cb[];
	private int nChars, nextChar;

	private static int defaultCharBufferSize = 8192;

	public AuditLoggerWriter(Writer out) {
		this(out, defaultCharBufferSize);
	}

	public AuditLoggerWriter(Writer out, int sz) {
		super(out);
		if (sz <= 0)
			throw new IllegalArgumentException("Buffer size <= 0");
		this.out = out;
		cb = new char[sz];
		nChars = sz;
		nextChar = 0;
	}

	/** Check to make sure that the stream has not been closed */
	private void ensureOpen() throws IOException {
		if (out == null)
			throw new IOException("Stream closed");
	}

	void flushBuffer() throws IOException {
		synchronized (lock) {
			ensureOpen();			
			
			if (nextChar == 0)
				return;
			out.write(cb, 0, nextChar);
			out.flush();
			nextChar = 0;
		}
	}

	public void write(int c) throws IOException {
		synchronized (lock) {
			ensureOpen();
			
			if (nextChar >= nChars)
				flushBuffer();
			cb[nextChar++] = (char) c;
		}
	}

	public void write(char cbuf[], int off, int len) throws IOException {
		synchronized (lock) {
			ensureOpen();
			if (cbuf == null)
				return;
			
			if ((off < 0) || (off > cbuf.length) || (len <= 0) || 
				((off + len) > cbuf.length) || ((off + len) < 0)) {
				return;
			}

			if (len >= nChars) {
				/*
				 * If the request length exceeds the size of the output buffer,
				 * flush the buffer and then write the data directly. In this
				 * way buffered streams will cascade harmlessly.
				 */
				flushBuffer();
				out.write(cbuf, off, len);
				return;
			}

			if (nextChar + len >= nChars)
				flushBuffer();
			
			System.arraycopy(cbuf, off, cb, nextChar, len);				
			nextChar += len;
		}
	}

	public void write(String s, int off, int len) throws IOException {
		synchronized (lock) {
			ensureOpen();

			if (s == null)
				return;
			
			if ((off < 0) || (off > s.length()) || (len <= 0) || 
				((off + len) > s.length()) || ((off + len) < 0)) {
				return;
			}

			if (len >= nChars) {
				/*
				 * If the request length exceeds the size of the output buffer,
				 * flush the buffer and then write the data directly. In this
				 * way buffered streams will cascade harmlessly.
				 */
				flushBuffer();
				out.write(s, off, len);
				return;
			}
			
			if (nextChar + len >= nChars)
				flushBuffer();

			s.getChars(off, len, cb, nextChar);			
			nextChar += len;
		}
	}

	/**
	 * Flush the stream.
	 * 
	 * @exception IOException
	 *                If an I/O error occurs
	 */
	public void flush() throws IOException {
		synchronized (lock) {
			flushBuffer();
			out.flush();
		}
	}

	/**
	 * Close the stream.
	 * 
	 * @exception IOException
	 *                If an I/O error occurs
	 */
	public void close() throws IOException {
		synchronized (lock) {
			if (out == null)
				return;
			flushBuffer();
			out.close();
			out = null;
			cb = null;
		}
	}
}
